/**
 * 微信JSSDK工具类
 * 用于生成微信公众号JSSDK配置所需的参数
 */

// 导入加密模块（需要安装：npm install crypto-js）
// 注意：实际项目中需要安装这个依赖
let CryptoJS;
try {
  CryptoJS = require('crypto-js');
} catch (error) {
  console.error('请安装crypto-js依赖: npm install crypto-js');
}

// 微信公众号配置信息
const wechatConfig = {
  appId: 'wxee39deb8d9fd37e3', // 已更新为实际appId
  appSecret: 'your_app_secret', // 替换为您的微信公众号appSecret
  accessTokenUrl: 'https://api.weixin.qq.com/cgi-bin/token',
  ticketUrl: 'https://api.weixin.qq.com/cgi-bin/ticket/getticket',
  debug: true, // 是否开启调试模式
  retryCount: 3, // 重试次数
  retryDelay: 1000 // 重试延迟（毫秒）
};

// 缓存accessToken和jsapiTicket
const cache = {
  accessToken: {
    value: '',
    expireTime: 0
  },
  jsapiTicket: {
    value: '',
    expireTime: 0
  }
};

/**
 * 生成随机字符串
 * @param {Number} length 字符串长度
 * @returns {String} 随机字符串
 */
function generateNonceStr(length = 16) {
  const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  let nonceStr = '';
  for (let i = 0; i < length; i++) {
    nonceStr += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return nonceStr;
}

/**
 * 生成时间戳
 * @returns {String} 时间戳字符串
 */
function generateTimestamp() {
  return Math.floor(Date.now() / 1000).toString();
}

/**
 * 处理URL，确保符合微信要求
 * @param {String} url 原始URL
 * @returns {String} 处理后的URL
 */
function processUrl(url) {
  try {
    // 移除hash部分
    url = url.split('#')[0];
    
    // 解析URL
    const urlObj = new URL(url);
    
    // 移除不必要的查询参数
    const params = new URLSearchParams(urlObj.search);
    params.delete('timestamp');
    params.delete('nonceStr');
    params.delete('signature');
    
    // 重新构建URL
    urlObj.search = params.toString();
    return urlObj.toString();
  } catch (error) {
    console.error('URL处理失败:', error);
    return url;
  }
}

/**
 * 重试函数执行 - 用于网络请求重试
 * @param {Function} fn 要重试的异步函数
 * @param {Number} maxRetries 最大重试次数
 * @param {Number} delay 重试延迟时间（毫秒）
 * @returns {Promise} 函数执行结果
 */
async function retry(fn, maxRetries = wechatConfig.retryCount, delay = wechatConfig.retryDelay) {
  let lastError;
  for (let i = 0; i < maxRetries; i++) {
    try {
      if (i > 0) {
        console.log(`重试第 ${i} 次...`);
      }
      return await fn();
    } catch (error) {
      console.error(`尝试 ${i + 1}/${maxRetries} 失败:`, error);
      lastError = error;
      if (i < maxRetries - 1) {
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }
  }
  throw lastError;
}

/**
 * 获取微信AccessToken
 * @param {Boolean} forceRefresh 是否强制刷新
 * @returns {Promise<String>} AccessToken
 */
async function getAccessToken(forceRefresh = false) {
  const logPrefix = '【getAccessToken】';
  try {
    // 如果缓存中有未过期的accessToken且不强制刷新，直接返回
    if (!forceRefresh && cache.accessToken.value && cache.accessToken.expireTime > Date.now()) {
      if (wechatConfig.debug) {
        console.log(`${logPrefix}使用缓存的AccessToken:`, cache.accessToken.value);
      }
      return cache.accessToken.value;
    }
    
    // 否则重新获取
    const url = `${wechatConfig.accessTokenUrl}?grant_type=client_credential&appid=${wechatConfig.appId}&secret=${wechatConfig.appSecret}`;
    if (wechatConfig.debug) {
      console.log(`${logPrefix}请求AccessToken URL:`, url);
    }
    
    // 开发环境返回模拟数据
    if (process.env.NODE_ENV === 'development') {
      const mockToken = 'mock_access_token_' + Math.random().toString(36).substr(2, 15);
      cache.accessToken = {
        value: mockToken,
        expireTime: Date.now() + 7000 * 1000 // 模拟7000秒有效期
      };
      if (wechatConfig.debug) {
        console.log(`${logPrefix}开发环境使用模拟AccessToken:`, mockToken);
      }
      return mockToken;
    }

    return await retry(async () => {
      const response = await fetch(url);
      const data = await response.json();
      
      if (wechatConfig.debug) {
        console.log(`${logPrefix}获取AccessToken响应:`, data);
      }
      
      if (data.access_token) {
        // 缓存accessToken，设置过期时间提前5分钟过期
        cache.accessToken = {
          value: data.access_token,
          expireTime: Date.now() + (data.expires_in - 300) * 1000
        };
        return data.access_token;
      } else {
        throw new Error(data.errmsg || '获取accessToken失败');
      }
    });
  } catch (error) {
    console.error(`${logPrefix}获取微信accessToken失败:`, error);
    // 开发环境下返回模拟token避免中断
    if (process.env.NODE_ENV === 'development') {
      const mockToken = 'mock_error_token_' + Math.random().toString(36).substr(2, 15);
      console.warn(`${logPrefix}开发环境下使用模拟错误Token:`, mockToken);
      return mockToken;
    }
    throw error;
  }
}

/**
 * 获取微信jsapi_ticket
 * @param {Boolean} forceRefresh 是否强制刷新
 * @returns {Promise<String>} jsapi_ticket
 */
async function getJsapiTicket(forceRefresh = false) {
  const logPrefix = '【getJsapiTicket】';
  try {
    // 如果缓存中有未过期的jsapiTicket且不强制刷新，直接返回
    if (!forceRefresh && cache.jsapiTicket.value && cache.jsapiTicket.expireTime > Date.now()) {
      if (wechatConfig.debug) {
        console.log(`${logPrefix}使用缓存的JsapiTicket:`, cache.jsapiTicket.value);
      }
      return cache.jsapiTicket.value;
    }
    
    // 开发环境返回模拟数据
    if (process.env.NODE_ENV === 'development') {
      const mockTicket = 'mock_jsapi_ticket_' + Math.random().toString(36).substr(2, 15);
      cache.jsapiTicket = {
        value: mockTicket,
        expireTime: Date.now() + 7000 * 1000 // 模拟7000秒有效期
      };
      if (wechatConfig.debug) {
        console.log(`${logPrefix}开发环境使用模拟JsapiTicket:`, mockTicket);
      }
      return mockTicket;
    }
    
    // 否则重新获取
    return await retry(async () => {
      const accessToken = await getAccessToken();
      const url = `${wechatConfig.ticketUrl}?access_token=${accessToken}&type=jsapi`;
      
      if (wechatConfig.debug) {
        console.log(`${logPrefix}请求JsapiTicket URL:`, url);
      }
      
      const response = await fetch(url);
      const data = await response.json();
      
      if (wechatConfig.debug) {
        console.log(`${logPrefix}获取JsapiTicket响应:`, data);
      }
      
      if (data.ticket) {
        // 缓存jsapiTicket，设置过期时间提前5分钟过期
        cache.jsapiTicket = {
          value: data.ticket,
          expireTime: Date.now() + (data.expires_in - 300) * 1000
        };
        return data.ticket;
      } else {
        throw new Error(data.errmsg || '获取jsapi_ticket失败');
      }
    });
  } catch (error) {
    console.error(`${logPrefix}获取微信jsapi_ticket失败:`, error);
    // 开发环境下返回模拟ticket避免中断
    if (process.env.NODE_ENV === 'development') {
      const mockTicket = 'mock_error_ticket_' + Math.random().toString(36).substr(2, 15);
      console.warn(`${logPrefix}开发环境下使用模拟错误Ticket:`, mockTicket);
      return mockTicket;
    }
    throw error;
  }
}

/**
 * 生成微信JSSDK签名
 * @param {String} url 当前页面URL，不包含#号后面的部分
 * @param {Boolean} forceRefresh 是否强制刷新票据
 * @returns {Promise<Object>} 签名配置对象
 */
async function generateConfig(url, forceRefresh = false) {
  const logPrefix = '【generateConfig】';
  try {
    if (!url) {
      throw new Error('URL参数不能为空');
    }
    
    // 处理URL，确保格式符合微信要求
    const processedUrl = processUrl(url);
    
    if (wechatConfig.debug) {
      console.log(`${logPrefix}原始URL:`, url);
      console.log(`${logPrefix}处理后的URL:`, processedUrl);
    }
    
    const jsapiTicket = await getJsapiTicket(forceRefresh);
    const nonceStr = generateNonceStr();
    const timestamp = Math.floor(Date.now() / 1000).toString(); // 确保是字符串类型
    
    // 按照微信规定的格式组合参数（注意顺序）
    const string1 = [
      `jsapi_ticket=${jsapiTicket}`,
      `noncestr=${nonceStr}`,
      `timestamp=${timestamp}`,
      `url=${processedUrl}`
    ].sort().join('&');
    
    if (wechatConfig.debug) {
      console.log(`${logPrefix}签名原始字符串:`, string1);
    }
    
    // 使用SHA1进行签名
    let signature;
    if (CryptoJS) {
      // 使用CryptoJS计算签名
      signature = CryptoJS.SHA1(string1).toString();
      
      // 记录详细的签名过程，便于调试
      if (wechatConfig.debug) {
        console.log(`${logPrefix}SHA1签名过程：`);
        console.log(`  - 原始字符串: ${string1}`);
        console.log(`  - SHA1结果: ${signature}`);
        
        // 直接使用crypto-js的方法验证签名
        const validationHash = CryptoJS.SHA1(string1).toString();
        console.log(`  - 验证签名: ${validationHash}`);
        console.log(`  - 签名是否匹配: ${signature === validationHash ? '是' : '否'}`);
      }
    } else {
      throw new Error('缺少CryptoJS依赖，无法生成签名');
    }
    
    // 返回配置对象
    const config = {
      appId: wechatConfig.appId,
      timestamp,
      nonceStr,
      signature
    };
    
    if (wechatConfig.debug) {
      console.log(`${logPrefix}完整的配置对象:`, config);
    }
    
    return config;
  } catch (error) {
    console.error(`${logPrefix}生成微信JSSDK配置失败:`, error);
    throw error;
  }
}

/**
 * 清除所有缓存
 * 用于强制重新获取所有票据
 */
function clearCache() {
  if (wechatConfig.debug) {
    console.log('清除所有微信票据缓存');
  }
  cache.accessToken = { value: '', expireTime: 0 };
  cache.jsapiTicket = { value: '', expireTime: 0 };
}

/**
 * 检测URL是否符合微信签名要求
 * @param {String} url 要检测的URL
 * @returns {Object} 检测结果
 */
function checkUrl(url) {
  const result = {
    isValid: true,
    message: '有效URL',
    url: url
  };
  
  if (!url) {
    result.isValid = false;
    result.message = 'URL不能为空';
    return result;
  }
  
  // 检查URL是否包含#号 
  if (url.includes('#')) {
    result.isValid = false;
    result.message = 'URL包含#号，微信签名URL不能包含#后面的部分';
    result.recommendedUrl = url.split('#')[0];
  }
  
  // 检查URL是否是完整URL（包含协议和域名）
  if (!url.startsWith('http://') && !url.startsWith('https://')) {
    result.isValid = false;
    result.message = 'URL缺少协议，必须是完整URL（包含http://或https://）';
  }
  
  // 检查URL中是否有可能导致签名问题的特殊字符
  try {
    const urlObj = new URL(url);
    // 检查路径是否编码正确
    if (urlObj.pathname !== decodeURIComponent(urlObj.pathname)) {
      result.isValid = false;
      result.message = 'URL路径中包含未正确编码的特殊字符';
      // 尝试修复URL
      const fixedUrl = urlObj.origin + encodeURI(decodeURIComponent(urlObj.pathname)) + urlObj.search;
      result.recommendedUrl = fixedUrl;
    }
  } catch (e) {
    result.isValid = false;
    result.message = '无效的URL格式';
  }
  
  return result;
}

/**
 * 诊断工具 - 用于排查JSSDK配置问题
 * @param {String} url 当前页面URL
 * @returns {Promise<Object>} 诊断结果
 */
async function diagnose(url) {
  const result = {
    timestamp: new Date().toISOString(),
    environment: process.env.NODE_ENV,
    config: { ...wechatConfig, appSecret: '***' }, // 保护敏感信息
    url: url,
    checks: []
  };
  
  // 检查URL
  const urlCheck = checkUrl(url);
  result.checks.push({
    name: 'URL检查',
    passed: urlCheck.isValid,
    message: urlCheck.message
  });
  
  if (!urlCheck.isValid && urlCheck.recommendedUrl) {
    url = urlCheck.recommendedUrl;
    result.fixedUrl = url;
  }
  
  // 检查处理后的URL
  const processedUrl = processUrl(url);
  result.checks.push({
    name: 'URL处理检查',
    passed: true,
    message: `处理后的URL: ${processedUrl}`,
    details: {
      original: url,
      processed: processedUrl
    }
  });
  
  // 检查CryptoJS
  result.checks.push({
    name: 'CryptoJS检查',
    passed: !!CryptoJS,
    message: CryptoJS ? 'CryptoJS已正确加载' : '缺少CryptoJS依赖，无法生成正确签名'
  });
  
  // 检查accessToken
  try {
    const accessToken = await getAccessToken(true);
    result.checks.push({
      name: 'AccessToken检查',
      passed: !!accessToken,
      message: accessToken ? '成功获取AccessToken' : '获取AccessToken返回空值'
    });
  } catch (error) {
    result.checks.push({
      name: 'AccessToken检查',
      passed: false,
      message: `获取AccessToken失败: ${error.message}`
    });
  }
  
  // 检查jsapiTicket
  try {
    const jsapiTicket = await getJsapiTicket(true);
    result.checks.push({
      name: 'JsapiTicket检查',
      passed: !!jsapiTicket,
      message: jsapiTicket ? '成功获取JsapiTicket' : '获取JsapiTicket返回空值'
    });
  } catch (error) {
    result.checks.push({
      name: 'JsapiTicket检查',
      passed: false,
      message: `获取JsapiTicket失败: ${error.message}`
    });
  }
  
  // 生成签名
  try {
    const config = await generateConfig(url, true);
    result.checks.push({
      name: '签名生成检查',
      passed: !!config.signature,
      message: config.signature ? '成功生成签名' : '生成的签名为空'
    });
    result.generatedConfig = config;
  } catch (error) {
    result.checks.push({
      name: '签名生成检查',
      passed: false,
      message: `生成签名失败: ${error.message}`
    });
  }
  
  // 汇总结果
  const allPassed = result.checks.every(check => check.passed);
  result.status = allPassed ? '通过' : '失败';
  result.message = allPassed
    ? '所有检查通过，JSSDK配置应该可以正常工作'
    : '存在问题，请查看详细检查结果';
  
  return result;
}

module.exports = {
  generateConfig,
  getAccessToken,
  getJsapiTicket,
  wechatConfig,
  clearCache,
  diagnose,
  checkUrl,
  processUrl
}; 